package tuding.android.bigplanettracks.maps.ui;

import java.util.ArrayList;
import java.util.List;

import tuding.android.bigplanettracks.BigPlanet;
import tuding.android.bigplanettracks.R;
import tuding.android.bigplanettracks.maps.AbstractCommand;
import tuding.android.bigplanettracks.maps.MarkerManager;
import tuding.android.bigplanettracks.maps.Marker;
import tuding.android.bigplanettracks.maps.PhysicMap;
import tuding.android.bigplanettracks.maps.RawTile;
import tuding.android.bigplanettracks.maps.db.DAO;
import tuding.android.bigplanettracks.maps.tuding.ActionNotes;
import tuding.android.bigplanettracks.maps.tuding.InfoWindow;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
//import android.util.Log;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.Toast;
import android.view.GestureDetector;
import android.view.GestureDetector.OnDoubleTapListener;
import android.view.GestureDetector.OnGestureListener;

/**
 * 
 * @author hudvin
 * 
 */
public class MapControl extends RelativeLayout {

	private static final int TILE_SIZE = 256;

	public static final int ZOOM_MODE = 0;

	public static final int SELECT_MODE = 1;

	private int mapMode = ZOOM_MODE;

	private Panel main;
	private View footer;

	private Bitmap cb = null;
	
	private Canvas cs;

	//private DoubleClickDetector dcDetector = new DoubleClickDetector();

	private PhysicMap pmap;


	/*
	 * Toolbar with zoom cont
	 */
	public boolean needDraw = true;
	
	private ZoomPanel zoomPanel;

	private boolean isNew = true;

	private final static int BCG_CELL_SIZE = 16;

	private OnMapLongClickListener onMapLongClickListener;

	private MarkerManager markerManager;

	public static Bitmap CELL_BACKGROUND = BitmapUtils.drawBackground(BCG_CELL_SIZE, TILE_SIZE, TILE_SIZE);

	public static Bitmap EMPTY_BACKGROUND = BitmapUtils.drawEmptyBackground(TILE_SIZE);

	private Point scalePoint = new Point();

	private SmoothZoomEngine szEngine;

	public Handler h;
	
	private Context context;
	
	private final float density = BigPlanet.density;
	private int bestZoomFromDB = -2;
	//private int initialZoomFromDB = -2;
	//private int minZoomFromDB = 10; // (tile.Zoom = 17-10 = 7)
	
	private List<Marker> markersTemp = new ArrayList<Marker>();

	//private long touchTime = 0;
	
	private GestureDetector mGestureDetector;
	private boolean doubletap_event_active = false;
	private boolean longpress_event_active = false;
	private boolean up2_event_active = false;

	// MyTracks
	private final Drawable arrow[] = new Drawable[18];
	private final int arrowWidth, arrowHeight;
	public static InfoWindow infowin = null;
	
	/**
	 * Constructor
	 * 
	 * @param context
	 * @param width
	 * @param height
	 * @param startTile
	 */
	public MapControl(Context context, int width, int height,
			RawTile startTile, MarkerManager markerManager) {
		super(context);
		this.context = context;
		scalePoint.set(width / 2, height / 2);
		this.markerManager = markerManager;
		buildView(width, height, startTile);
		
		
		
		arrow[0] = context.getResources().getDrawable(R.drawable.arrow_0);
		arrow[1] = context.getResources().getDrawable(R.drawable.arrow_20);
		arrow[2] = context.getResources().getDrawable(R.drawable.arrow_40);
		arrow[3] = context.getResources().getDrawable(R.drawable.arrow_60);
		arrow[4] = context.getResources().getDrawable(R.drawable.arrow_80);
		arrow[5] = context.getResources().getDrawable(R.drawable.arrow_100);
		arrow[6] = context.getResources().getDrawable(R.drawable.arrow_120);
		arrow[7] = context.getResources().getDrawable(R.drawable.arrow_140);
		arrow[8] = context.getResources().getDrawable(R.drawable.arrow_160);
		arrow[9] = context.getResources().getDrawable(R.drawable.arrow_180);
		arrow[10] = context.getResources().getDrawable(R.drawable.arrow_200);
		arrow[11] = context.getResources().getDrawable(R.drawable.arrow_220);
		arrow[12] = context.getResources().getDrawable(R.drawable.arrow_240);
		arrow[13] = context.getResources().getDrawable(R.drawable.arrow_260);
		arrow[14] = context.getResources().getDrawable(R.drawable.arrow_280);
		arrow[15] = context.getResources().getDrawable(R.drawable.arrow_300);
		arrow[16] = context.getResources().getDrawable(R.drawable.arrow_320);
		arrow[17] = context.getResources().getDrawable(R.drawable.arrow_340);
		arrowWidth = arrow[BigPlanet.lastHeading].getIntrinsicWidth();
		arrowHeight = arrow[BigPlanet.lastHeading].getIntrinsicHeight();
		for (int i = 0; i <= 17; i++) {
			arrow[i].setBounds(0, 0, arrowWidth, arrowHeight);
		}
		
		final Handler updateControlsHandler = new Handler() {

			@Override
			public void handleMessage(Message msg) {

				switch (msg.what) {
				case 0:
					updateZoomControls();
					break;
				}
				super.handleMessage(msg);
				postInvalidate();
			}
		};
		
		szEngine = SmoothZoomEngine.getInstance();
		szEngine.setReloadMapCommand(new AbstractCommand() {

			public void execute(Object object) {
				double sf = (Float) object;
				pmap.zoomS(sf);
				updateControlsHandler.sendEmptyMessage(0);
			}

		});
		szEngine.setUpdateScreenCommand(new AbstractCommand() {
			
			public void execute(Object object) {
				pmap.scaleFactor = (Float) object;
				postInvalidate();
			}

		});
	}

	public int getMapMode() {
		return mapMode;
	}

	/**
	 * 
	 * @param mapMode
	 */
	public void setMapMode(int mapMode) {
		this.mapMode = mapMode;
		//updateZoomControls();
	}

	public void setOnMapLongClickListener(
			OnMapLongClickListener onMapLongClickListener) {
		this.onMapLongClickListener = onMapLongClickListener;
	}

	/**
	 * 
	 * @param width
	 * @param height
	 */
	public void setSize(int width, int height) {
		if (main != null) {
			removeView(main);
			removeView(footer);
		}
		pmap.resetCell(width, height);
		buildView(width, height, pmap.getDefaultTile());
	}

	/**
	 * 
	 * @return
	 */
	public PhysicMap getPhysicalMap() {
		return pmap;
	}

	public void goTo(int x, int y, int z, int offsetX, int offsetY) {
		//Log.i("EVENT", "mapcontrol goto: "+offsetX+" "+offsetY);
		if(getPhysicalMap().getTileResolver().getMapSourceId() <= 2) {
			Point offsetfix = MarkerManager.getGMOffset(new RawTile(x, y, z, -1));
			offsetX += offsetfix.x;
			offsetY += offsetfix.y;
			while (offsetX > 256) {
				x ++;
				offsetX -= 256;
			} 
			while (offsetX < 0) {
				x --;
				offsetX += 256;
			}
			while (offsetY > 256) {
				y ++;
				offsetY -= 256;
			} 
			while (offsetY < 0) {
				y --;
				offsetY += 256;
			}
		}
		getPhysicalMap().goTo(x, y, z, offsetX, offsetY);
		//updateZoomControls();
		updateScreen(0);
	}

	/**
	 * 
	 * @param width
	 * @param height
	 * @param startTile
	 */
	private void buildView(int width, int height, RawTile startTile) {
		h = new Handler() {
			@Override
			public void handleMessage(Message msg) {
				updateZoomControls();
			}
		};
		
		//
		main = new Panel(this.getContext());
		//addView(main, 0, new ViewGroup.LayoutParams(width, height));
		addView(main, 0, new ViewGroup.LayoutParams(width, height)); 
		footer = View.inflate(context, R.layout.launchbar, null );
		final RelativeLayout.LayoutParams paramsForBottom = new RelativeLayout.LayoutParams(
				RelativeLayout.LayoutParams.FILL_PARENT,
				RelativeLayout.LayoutParams.WRAP_CONTENT);
		paramsForBottom.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
		addView(footer, paramsForBottom);
		

		if (zoomPanel == null) { 
			zoomPanel = new ZoomPanel(this.getContext());
			zoomPanel.setOnZoomOutClickListener(new OnClickListener() {
				public void onClick(View v) {
					BigPlanet.isMapInCenter = false;
					int zoomLevel = PhysicMap.getZoomLevel();
					if (zoomLevel >= 17) {
						return;
					}
					zoomPanel.setIsZoomOutEnabled(false); // avoid double click to cause grey screen
					scalePoint.set(pmap.getWidth() / 2, pmap.getHeight() / 2);
					smoothZoom(-1);
				}
			});

			zoomPanel.setOnZoomInClickListener(new OnClickListener() {
				public void onClick(View v) {
//					Log.i("MapControl", "OnZoomInClick");
					BigPlanet.isMapInCenter = false;
					int zoomLevel = PhysicMap.getZoomLevel();
					if (zoomLevel <= -2) {
						return;
					}
					zoomPanel.setIsZoomInEnabled(false); // avoid double click to cause grey screen
					scalePoint.set(pmap.getWidth() / 2, pmap.getHeight() / 2);
					smoothZoom(1);
				}
			});

			final RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
					RelativeLayout.LayoutParams.WRAP_CONTENT,
					RelativeLayout.LayoutParams.WRAP_CONTENT);
			params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
			addView(zoomPanel, params);

		}
		// Convert the dips to pixels 
		zoomPanel.setPadding((width - (int)(160*(density))) / 2, height - (int)(90*(density)), 0, 0);

		if (pmap == null) { 
			pmap = new PhysicMap(width, height, startTile, new AbstractCommand() {

				/**
				 * Callback, 
				 */
				@Override
				public void execute() {
					//Log.i("PMAP","PhysicMap is executed");
					updateScreen();
				}

			}, new AbstractCommand() {
				@Override
				public void execute() {
					if (main != null) {
						main.postInvalidate();
					}
				}
			});
		}
		pmap.quickHack();
	}

	private void smoothZoom(int direction) {
//		System.out.println(getPhysicalMap().getTileResolver().getLoaded());
//		szEngine.addToScaleQ(direction);
		// modified by Panyf @2011-8-31 
		double endScaleFactor;
		endScaleFactor = (direction == -1) ? szEngine.scaleFactor / 2 
				                           : szEngine.scaleFactor * 2;
		int z = PhysicMap.getZoomLevel();
		if ((direction == -1 && z < 17) || (direction == 1 && z > -2)) {
			if (!(endScaleFactor > 8000 || endScaleFactor < 125)) {
				synchronized (szEngine) {
						//szEngine.updateScreen.execute(new Float(endScaleFactor / 1000));
						szEngine.reloadMap.execute(new Float(endScaleFactor / 1000));
				}
			}
		}
	}


	public synchronized void updateScreen() {
		if (main != null) {
			//Log.i("TRACK", "updateScreen!: "+pmap.getGlobalOffset().x+" "+pmap.getGlobalOffset().y);
			main.postInvalidate();
			// Panyf: following 2 line is just update activty title
			// set type = -1 means, only update title
			Intent i = new Intent(BigPlanet.UpdateScreenAction);
			i.putExtra("type", -1);
			context.sendBroadcast(i);
		}
	}
	
	public synchronized void updateScreen(int type) {
		if (main != null) {
			main.postInvalidate();
			//Log.i("TRACK", "updateScreen with type!: "+pmap.getGlobalOffset().x+" "+pmap.getGlobalOffset().y);
			Intent i = new Intent(BigPlanet.UpdateScreenAction);
			i.putExtra("type", type);
			context.sendBroadcast(i);
		}
	}
	
	private void invokeGoToMyLocation(double lat, double lon, int zoom) {
		Intent i = new Intent(BigPlanet.UpdateScreenAction);
		i.putExtra("type", 1);
		i.putExtra("lat", lat);
		i.putExtra("lon", lon);
		i.putExtra("zoom", zoom);
		context.sendBroadcast(i);
	}

	/**
	 * 
	 */
	public void updateZoomControls() {
		int zoomLevel = PhysicMap.getZoomLevel();
		markerManager.updateAll(zoomLevel);
		if (getMapMode() == MapControl.SELECT_MODE) {
			//zoomPanel.setVisibility(View.INVISIBLE);
		} else {
			zoomPanel.setVisibility(View.VISIBLE);
			if (zoomLevel >= 17) {
				zoomPanel.setIsZoomOutEnabled(false);
				zoomPanel.setIsZoomInEnabled(true);
			} else if (zoomLevel <= -2) {
				zoomPanel.setIsZoomOutEnabled(true);
				zoomPanel.setIsZoomInEnabled(false);
			} else { 
				zoomPanel.setIsZoomOutEnabled(true);
				zoomPanel.setIsZoomInEnabled(true);
			}
		}
	}

	/**
	 * 
	 * @param canvas
	 * @param paint
	 */
	private synchronized void doDraw(Canvas c, Paint paint, Paint paintline) {
		
		//Log.i("EVENT", "currentLoc-lat: "+BigPlanet.currentLocation.getLatitude()+" lon: "+BigPlanet.currentLocation.getLongitude());
		
		if (cb == null || cb.getHeight() != pmap.getHeight()) {
			cs = new Canvas(); 
			//if(cb == null) Log.i("ATTENTION","cb is null, so doDraw");
			//else Log.i("ATTENTION", "doDRAW: cb: " +cb.getHeight()+" pmap: "+pmap.getHeight());
			cb = Bitmap.createBitmap(pmap.getWidth(), pmap.getHeight(), Bitmap.Config.RGB_565);
			cs.setBitmap(cb);
		}
		
		
		
		//add by Panyf for prepare background color
		c.drawARGB(255, 128, 128, 128);
		

		
//		System.out.println("doDraw scaleFactor " + pmap.scaleFactor);
		Log.i("EVENT", "dodraw: "+pmap.getGlobalOffset().x+" "+pmap.getGlobalOffset().y);
		Bitmap tmpBitmap;
		for (int i = 0; i < pmap.cells.length+2; i++) {
			for (int j = 0; j < pmap.cells[0].length+2; j++) {
				if ((i >= 1 && i < pmap.cells.length+1) && ((j >= 1 && j < pmap.cells[0].length+1))) {
					tmpBitmap = pmap.getCell(i - 1, j - 1);
					if (tmpBitmap != null && !tmpBitmap.isRecycled()) {
						isNew = false;
						cs.drawBitmap(tmpBitmap, 
								(i - 1) * TILE_SIZE + pmap.getGlobalOffset().x, 
								(j - 1) * TILE_SIZE + pmap.getGlobalOffset().y, paint);
					}
				} else {
					//Log.i("EVENT", "scaleFactor is "+pmap.scaleFactor);
					if (pmap.scaleFactor == 1) {
						cs.drawBitmap(CELL_BACKGROUND, 
								(i - 1) * TILE_SIZE + pmap.getGlobalOffset().x, 
								(j - 1) * TILE_SIZE + pmap.getGlobalOffset().y, paint);
					} else {
						cs.drawBitmap(EMPTY_BACKGROUND, 
								(i - 1) * TILE_SIZE + pmap.getGlobalOffset().x, 
								(j - 1) * TILE_SIZE + pmap.getGlobalOffset().y, paint);
					}
				}
			}
		}
		
		// following routine is use to update line
		if(needDraw) { // only the bitmap need redraw when in moving
			//Log.i("PMAP","Need ReDraw!");
				int paintColor[] = new int[] {
						Color.RED,
						Color.BLUE,
						Color.CYAN,
					};
				
				int length = paintColor.length;
				
				boolean isEnabled[] = new boolean[] {
						true,
						BigPlanet.isGPSTracking,
						BigPlanet.isGPSTrackSaved,
					};
					 
				int markerType[] = new int[] {
						MarkerManager.START_GREEN_MARKER,
						MarkerManager.START_BLUE_MARKER,
						MarkerManager.START_BLUE_MARKER,
				};
			
				List<List<Marker>> allMarkerList = new ArrayList<List<Marker>>();
				allMarkerList.add(MarkerManager.markersDB);
				allMarkerList.add(MarkerManager.markersG);
				allMarkerList.add(MarkerManager.savedTrackG);
				
				//Log.i("TRACK", "needDraw and markersG size: "+MarkerManager.markersG.size()+" scaleFactor "+pmap.scaleFactor);
				
				
				boolean isDrawingMarkerG[] = new boolean[length];
				boolean isDrawing = false;
				for (int m = 0; m < allMarkerList.size(); m++) {
					if (isEnabled[m] && allMarkerList.get(m).size() != 0) {
						isDrawingMarkerG[m] = true;
						isDrawing = true;
					}
				}
				
				if (isDrawing && pmap.scaleFactor == 1) {
					for (int m = 0; m < allMarkerList.size(); m++) {
						if (isDrawingMarkerG[m]) {
							float x1 = 0, x2 = 0, y1 = 0, y2 = 0, startPointX = 0, startPointY = 0;
							boolean isDrawingStartMarker = false;
							boolean isSetStartPoint = false;
							
							paintline.setColor(paintColor[m]);
							paintline.setAlpha(100);

							List<Marker> markerList = allMarkerList.get(m);
							boolean markerisdrawed = false;
							for (int k = 0; k < markerList.size(); k++) {
								Marker marker = markerList.get(k);
								markerisdrawed = false;
								for (int i = 2; i < pmap.cells.length+2 && (!markerisdrawed); i++) {
									for (int j = 2; j < pmap.cells[0].length+2 && (!markerisdrawed); j++) {
											RawTile tile = pmap.getDefaultTile();
											int z = PhysicMap.getZoomLevel();
											int tileX = tile.x + (i - 2);
											int tileY = tile.y + (j - 2);
											
											
											boolean result = markerManager.isDrawingMarkerG(tileX, tileY, z, marker);
											if (result) {
												//Log.i("ZOOM", "tile: "+tileX+" "+tileY+" "+z+" "+((i-2)*2+j-2));
												markerisdrawed = true;
												if (isSetStartPoint) {
													x2 = (i - 2)* TILE_SIZE + pmap.getGlobalOffset().x + (int) marker.getOffset().x;
													y2 = (j - 2)* TILE_SIZE + pmap.getGlobalOffset().y + (int) marker.getOffset().y+3;
													if (x2 != 0 && y2 != 0) {
														cs.drawLine(x1, y1, x2, y2, paintline);
														x1 = x2;
														y1 = y2;
													}
												} else {
													x1 = (i - 2)* TILE_SIZE + pmap.getGlobalOffset().x + (int) marker.getOffset().x;
													y1 = (j - 2)* TILE_SIZE + pmap.getGlobalOffset().y + (int) marker.getOffset().y+3;
													if (x1 != 0 && y1 != 0) {
														startPointX = x1;
														startPointY = y1;
														isSetStartPoint = true;
													}	
												}
												
												if (k == 0) {
													isDrawingStartMarker = true;
												} else if (k == markerList.size()-1 ) {
													if(markerList.get(k).getMarkerActionType() == 0)
													    markerList.get(k).setMarkerImage(MarkerManager.images.get(markerType[m]+1)); // END_MY_TRACK_MARKER
													//Log.i("TRACK", "drawing the last marker: "+(x2 - markerList.get(k).getMarkerImage().getOffsetX()*density)+" "+(y2 - markerList.get(k).getMarkerImage().getOffsetY()*density));
													cs.drawBitmap(markerList.get(k).getMarkerImage().getImage(),
															x2 - markerList.get(k).getMarkerImage().getOffsetX()*density,
															y2 - markerList.get(k).getMarkerImage().getOffsetY()*density,paint);
												}
											}

									}
								}
							} // end for (int k = 0; k < markerGList.size(); k++)
							if (isDrawingStartMarker) {
								isDrawingStartMarker = false;
								if(BigPlanet.OMIT_MARKERS_FLAG == true && m > 0) // only the recording blue marker can be omit during recording
								    markerList.get(0).setMarkerImage(MarkerManager.images.get(MarkerManager.START_OMIT_MARKER)); // START MARKER with omition
								else 
									markerList.get(0).setMarkerImage(MarkerManager.images.get(markerType[m])); // START_MY_TRACK_MARKER
								cs.drawBitmap(markerList.get(0).getMarkerImage().getImage(),
										startPointX - markerList.get(0).getMarkerImage().getOffsetX()*density,
										startPointY - markerList.get(0).getMarkerImage().getOffsetY()*density,paint);
							}
						}
					}
				}
		} // needDraw
		

		if (pmap.scaleFactor == 1) {
			//if(!BigPlanet.isGPSTracking){
			{
				for (int i = 0; i < pmap.cells.length; i++) {
					for (int j = 0; j < pmap.cells[0].length; j++) {
							RawTile tile = pmap.getDefaultTile();
							int z = PhysicMap.getZoomLevel();
							int tileX = tile.x + (i);
							int tileY = tile.y + (j);
							
							//cs.drawLine(i * TILE_SIZE+pmap.getGlobalOffset().x, j * TILE_SIZE + pmap.getGlobalOffset().y, (i + 1) * TILE_SIZE+pmap.getGlobalOffset().x, j * TILE_SIZE +pmap.getGlobalOffset().y, paint);
							//cs.drawLine(i * TILE_SIZE+pmap.getGlobalOffset().x, j * TILE_SIZE + pmap.getGlobalOffset().y, i * TILE_SIZE+pmap.getGlobalOffset().x, (j + 1) * TILE_SIZE +pmap.getGlobalOffset().y, paint);
							//cs.drawText(""+tileX+" "+tileY, (float)(i * TILE_SIZE+pmap.getGlobalOffset().x), (float)(j * TILE_SIZE + pmap.getGlobalOffset().y), paint);
							
							List<Marker> markers = markerManager.getMarkers(tileX, tileY, z);
							List<Marker> Gmarkers = markerManager.getGMarkers(tileX, tileY, z);
							List<Marker> DBmarkers = markerManager.getDBMarkers(tileX, tileY, z);
							List<Marker> savedGmarkers = markerManager.getSavedGMarkers(tileX, tileY, z);
							List<Marker> markersRecord = markerManager.getMarkersRecord(tileX, tileY, z);
							//Log.i("Recording", "---->markersRecord size "+markersRecord.size());
							//Log.i("MARKER", "Gmarkers size is "+Gmarkers.size()+" DBmarkers size is "+DBmarkers.size()+" xyz "+tileX+" "+tileY+" "+z);
							
							Gmarkers.addAll(markersRecord);
							Gmarkers.addAll(markers);
							Gmarkers.addAll(savedGmarkers);
							Gmarkers.addAll(DBmarkers);
							
							
							for (Marker marker : Gmarkers) {
								//Log.i("MARKER", "dodraw: I am drawing markers: type is "+marker.getMarkerType()+" tile: "+tileX+" "+tileY);
								//Log.i("EVENT", "dodraw: size of markers list is "+markers.size()+" : tileX: "+tileX+" tileY: "+tileY+" Z: "+z+" offsetX: "+marker.offset.x+" offsetY: "+marker.offset.y);
							    if(marker.getMarkerType() == 0) { // default type: for self	
							    	if (BigPlanet.currentLocation != null) {
							    		Drawable drawable = arrow[BigPlanet.lastHeading];
							    		float leftX = i * TILE_SIZE	+ pmap.getGlobalOffset().x
					    				              + (float) marker.getOffset().x;
							    		float leftY = j * TILE_SIZE + pmap.getGlobalOffset().y
					    				              + (float) marker.getOffset().y;
							    		marker.offset_from_topleft = new tuding.android.bigplanettracks.maps.geoutils.Point((int)leftX, (int)leftY);
							    		leftX -= (drawable.getMinimumWidth()/2);
							    		leftY -= (drawable.getMinimumHeight()/2);
							    		cs.drawBitmap(((BitmapDrawable)drawable).getBitmap(), leftX, leftY, paint);
							    	} else { // if(marker.place.getName() != "BOOKMARK"){ // we need control the bookmark seperately
							    		float leftX = i * TILE_SIZE	+ pmap.getGlobalOffset().x
					    				              + (float) marker.getOffset().x;
				    		            float leftY = j * TILE_SIZE + pmap.getGlobalOffset().y
					    				              + (float) marker.getOffset().y;
				    		            marker.offset_from_topleft = new tuding.android.bigplanettracks.maps.geoutils.Point((int)leftX, (int)(leftY-16*density));
				    		            leftX -= marker.getMarkerImage().getOffsetX()*density;
				    		            leftY -= marker.getMarkerImage().getOffsetY()*density;
				    		            cs.drawBitmap(marker.getMarkerImage().getImage(), leftX, leftY, paint);
							    	}
							    }
								
								// for bookmark  
							    else if ((BigPlanet.showBookmarks == true && marker.getMarkerType() == MarkerManager.BOOKMARK_MARKER)
							            ) {
							    	float leftX = i * TILE_SIZE + pmap.getGlobalOffset().x
									              - (float) marker.getOffset().x;
				  		            float leftY = j * TILE_SIZE + pmap.getGlobalOffset().y
								                  - (float) marker.getOffset().y;
  		                            marker.offset_from_topleft = new tuding.android.bigplanettracks.maps.geoutils.Point((int)(leftX), (int)(leftY));
  		                            leftX -= marker.getMarkerImage().getOffsetX()*density;
  		                            leftY -= marker.getMarkerImage().getOffsetY()*density;
									//Log.i("EVENT","getGlobalOffset: "+pmap.getGlobalOffset().x+" "+pmap.getGlobalOffset().y+" marker.getOffset:"+marker.getOffset().x+" "+marker.getOffset().y);
									cs.drawBitmap(marker.getMarkerImage().getImage(), leftX, leftY, paint);
								}
							    // for search results
							    else if (( BigPlanet.showSearchResults == true && marker.getMarkerType() == MarkerManager.SEARCH_MARKER)
							    		|| marker.getMarkerType() == MarkerManager.ACTION_NOTES_MARKER	
							    		|| marker.getMarkerType() == MarkerManager.ACTION_PHOTO_MARKER
							            ) {
							    	float leftX = i * TILE_SIZE + pmap.getGlobalOffset().x
									              + (float) marker.getOffset().x;
				  		            float leftY = j * TILE_SIZE + pmap.getGlobalOffset().y
									              + (float) marker.getOffset().y;
			                        marker.offset_from_topleft = new tuding.android.bigplanettracks.maps.geoutils.Point((int)(leftX+4*density), (int)(leftY-18*density));
			                        leftX -= marker.getMarkerImage().getOffsetX()*density;
			                        leftY -= marker.getMarkerImage().getOffsetY()*density;
						    	    //Log.i("MARKER", "dodraw: I am drawing markers: type is "+marker.getMarkerType());
									cs.drawBitmap(marker.getMarkerImage().getImage(), leftX, leftY, paint);
								}
								
							    else if(marker.getMarkerType() == MarkerManager.RECORDED_MARKER) {
							    	//Log.i("Recording", "--->drawing the marker");
							    	float leftX = i * TILE_SIZE	+ pmap.getGlobalOffset().x
	    				              			  + (float) marker.getOffset().x;
						    		float leftY = j * TILE_SIZE + pmap.getGlobalOffset().y
				    				              + (float) marker.getOffset().y;
						    		marker.offset_from_topleft = new tuding.android.bigplanettracks.maps.geoutils.Point((int)leftX, (int)leftY);
						    		leftX -= marker.getMarkerImage().getOffsetX()*density;
			                        leftY -= marker.getMarkerImage().getOffsetY()*density;
						    		cs.drawBitmap(marker.getMarkerImage().getImage(), leftX, leftY, paint);
							    }
							    
							}
						}
				}
			}
		} // end if (pmap.scaleFactor == 1)


		Matrix matr = new Matrix();
		matr.postScale((float) pmap.scaleFactor, (float) pmap.scaleFactor, scalePoint.x, scalePoint.y);
		c.drawBitmap(cb, matr, paint);
	}

	@Override
	protected void onAttachedToWindow() {
		super.onAttachedToWindow();
		new Thread() {
			@Override
			public void run() {
				while (isNew) {
					try {
						Thread.sleep(100);
						postInvalidate();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}

		}.start();
	}

	/**
     * 
	 * @author hudvin
	 * 
	 */
	class Panel extends View {
		Paint paint;
		Paint paintline;
		private double mTouchDiagonalSize;
		private double mTouchScale;
		private double mDown2X;
		private double mDown2Y;
		private double mDown1X;
		private double mDown1Y;
		//private double mDownX;
		//private double mDownY;

		public Panel(Context context) {
			super(context);
			paint = new Paint();
			paintline = new Paint();
			paintline.setStrokeWidth(5);
			paintline.setAntiAlias(true);
			paintline.setDither(true);
			paintline.setStyle(Paint.Style.FILL);
			paintline.setStrokeJoin(Paint.Join.MITER);
			mGestureDetector = new GestureDetector( context, new TudingGestureListener() );
			mTouchDiagonalSize = 0;
			mTouchScale = 1;
			mDown2X = 0;
			mDown2Y = 0;
			mDown1X = 0;
			mDown1Y = 0;

		}

		@Override
		protected void onDraw(Canvas canvas) {
			super.onDraw(canvas);
			
			//if(pmap.getTileResolver().getLoaded() > 0 && pmap.getTileResolver().getLoaded() < pmap.totalCells) return;
			
			//Log.i("TRACK", "onDraw is called, Deftile: x is "+pmap.defTile.x+", y is "+pmap.defTile.y+" z is "+pmap.defTile.z);
			//Log.i("TRACK", "onDraw is called:"+pmap.getGlobalOffset().x+" "+pmap.getGlobalOffset().y);
			//Log.i("PMAP", "onDraw: "+BigPlanet.autoDisplayDB+" "+MarkerManager.markersDB.size()+" "+MarkerManager.markers.size());
			if (BigPlanet.autoDisplayDB && MarkerManager.markersDB.size()!=0 && MarkerManager.markers.size()!=0) {
				if(BigPlanet.autoDisplayDBforMarker){
					markersTemp.add(MarkerManager.markers.get(0));
				}
				BigPlanet.autoDisplayDBforMarker = false;
				
				BigPlanet.autoDisplayDB = false;
				BigPlanet.autoDisplayDBforMarker = true;
				invokeGoToMyLocation(BigPlanet.autoDisplayDB_Lat, BigPlanet.autoDisplayDB_Lon, BigPlanet.fittingZoom);
				doDraw(canvas, paint, paintline);
				/*
				boolean check = checkMarkersDBforDisplay();
				if (bestZoomFromDB<minZoomFromDB && !check) {
					bestZoomFromDB = bestZoomFromDB+1;
					invokeGoToMyLocation(BigPlanet.autoDisplayDB_Lat, BigPlanet.autoDisplayDB_Lon, bestZoomFromDB);
					// It shows the progress of zooming out and can be disabled to speedup.
					doDraw(canvas, paint);
				} else {
					BigPlanet.autoDisplayDB = false;
					BigPlanet.autoDisplayDBforMarker = true;
					invokeGoToMyLocation(BigPlanet.autoDisplayDB_Lat, BigPlanet.autoDisplayDB_Lon, bestZoomFromDB+1);
					doDraw(canvas, paint);
					bestZoomFromDB = initialZoomFromDB; // initial value
				}*/
			} else {
				if (BigPlanet.autoDisplayDBforMarker && MarkerManager.markersDB.size()!=0) {
					BigPlanet.showBookmarks = false;
					MarkerManager.markers.clear();
					if (!BigPlanet.clearYellowPersonMarker) {
						//Log.i("EVENT", "MM.markers is adding: "+markersTemp.get(0).tile.x+" "+markersTemp.get(0).tile.y);
						MarkerManager.markers.add(markersTemp.get(0));
					} else {
						// clear the yellow person marker after loading the track from DB
						// don't execute markers.add()
						BigPlanet.clearYellowPersonMarker = false;
					}
					BigPlanet.autoDisplayDBforMarker = false;
				}
				doDraw(canvas, paint, paintline);
			}
		}
		
		public boolean checkMarkersDBforDisplay() {
			// check all points
			int factor = 1;
			int num = MarkerManager.markersDB.size();
			// quick check
			if (num > 40) {
				factor = 10;
				num = Math.round(num/factor);
			}
			int countDB = 0;
			for (int k=0; k < num; k++) {
				int index = k*factor;
				for (int i = 2; i < pmap.cells.length+2; i++) {
					for (int j = 2; j < pmap.cells[0].length+2; j++) {
						if ((i > 1 && i < pmap.cells.length+2) && ((j > 1 && j < pmap.cells[0].length+2))) {
							RawTile tile = pmap.getDefaultTile();
							int tileX = tile.x + (i - 2);
							int tileY = tile.y + (j - 2);

							try {
								if (MarkerManager.markersDB.get(index).tile.x == tileX && 
										MarkerManager.markersDB.get(index).tile.y == tileY && 
										MarkerManager.markersDB.get(index).tile.z == bestZoomFromDB) {
									countDB = countDB + 1;
								}	
							} catch (IndexOutOfBoundsException e) {
								if (MarkerManager.markersDB.get(index-1).tile.x == tileX && 
										MarkerManager.markersDB.get(index-1).tile.y == tileY && 
										MarkerManager.markersDB.get(index-1).tile.z == bestZoomFromDB) {
									countDB = countDB + 1;
								}
							}
						}
					}
				}
			}
//			Log.i("MapControl", "auto map zooming out: z="+(17-bestZoomFromDB)+", "+ countDB +"="+num);
			if (countDB == num) {
				return true;
			} else {
				return false;
			}
		}
		

		/**
		 */
		@Override
		public boolean onTouchEvent(final MotionEvent event) {
			// handle one tap/ double tap event
			//Log.i("TRACK", "onTouchEvent is active!!!");
			mGestureDetector.onTouchEvent(event);
			
			switch (event.getAction()) {
			/*ACTION_MOVE, ACTION_POINT_DOWNUP is use to calc two fingure zoom event*/
			case MotionEvent.ACTION_POINTER_DOWN:
			case MotionEvent.ACTION_POINTER_2_DOWN:
				//Toast.makeText(context, "POINTER_DONW", Toast.LENGTH_SHORT).show();
				try{
				    onDown2(event, event.getX(event.findPointerIndex(0)), event.getY(event.findPointerIndex(0)), event.getX(event.findPointerIndex(1)), event.getY(event.findPointerIndex(1)));
				} catch(Exception e){}
				break;
			case MotionEvent.ACTION_POINTER_UP:
			case MotionEvent.ACTION_POINTER_2_UP:
				onUp2(event);
				break;
			case MotionEvent.ACTION_DOWN:
				//Log.i("EVENT", "ACTION_DOWN event is active.");
				//mDownX = event.getX();
				//mDownY = event.getY();
				pmap.getNextMovePoint().set(
						main.getRealPoint(event.getX(), scalePoint.x),
						main.getRealPoint(event.getY(), scalePoint.y));
				break;
			case MotionEvent.ACTION_MOVE:
				// if up2 event is active, which means user just with-draw one finger, the other finger is still on screen;
				// we donot want the left finger cause move event, which will cause mess up.
				// conditions will degrate perf dramtically
				//if(Math.abs(event.getX()-mDownX)+Math.abs(event.getY()-mDownY) > 6)
				//if(up2_event_active == false && longpress_event_active == false) {
				//Toast.makeText(context, "ACTION_MOVE", Toast.LENGTH_SHORT).show();
				BigPlanet.resetPressAgainFlag();
				clearInfoWin();
				if(up2_event_active == false) {
					try {
						if(BigPlanet.isFollowMode){
							Toast.makeText(MapControl.this.context, R.string.auto_follow_disabled, Toast.LENGTH_SHORT).show();
							BigPlanet.isFollowMode = false;
						}
						// if doubletapping, then it should not be a move event, otherwise, the map cannot updated: see pmap.update().
						if(doubletap_event_active == false) pmap.inMove = true;
						if(event.getPointerCount() <= 1) {
							//Log.i("EVENT", "ACTION_MOVE event is active with 1 finger move.");
							needDraw = false;
						    pmap.moveCoordinates(
								main.getRealPoint(event.getX(), scalePoint.x), 
								main.getRealPoint(event.getY(), scalePoint.y)
								);
						}
						else if(event.getPointerCount() > 1){
							//Log.i("EVENT", "ACTION_MOVE event is active with 2 finger move.");
							final int index0 = event.findPointerIndex(0);
							final int index1 = event.findPointerIndex(1);
							if(index0 != -1 && index1 != -1){
								onMove2(event, event.getX(event.findPointerIndex(0)), event.getY(event.findPointerIndex(0)), event.getX(event.findPointerIndex(1)), event.getY(event.findPointerIndex(1)));
							}
						}
					} catch (Exception e){}
				}
				break;
			case MotionEvent.ACTION_UP:
				if(needDraw == false)needDraw = true;
				//Log.i("ACTION_UP", "Deftile: x is "+pmap.defTile.x+", y is "+pmap.defTile.y+" z is "+pmap.defTile.z);
				if(doubletap_event_active == true){
					//Log.i("EVENT", "ACTION_UP event active with doubletap!");
					doubletap_event_active = false;
					//Toast.makeText(context, "double tap event active", Toast.LENGTH_SHORT).show();
				}
				
				else if(longpress_event_active == true){
					//Log.i("EVENT", "ACTION_UP event active with longpress!");
					longpress_event_active = false;
					//Toast.makeText(context, "long press event active", Toast.LENGTH_SHORT).show();
				}
				
				else if(up2_event_active == true){
					//Log.i("EVENT", "ACTION_UP event active with up2 event!");
					up2_event_active = false;
				}
				
				else { // not double-tap
					//Toast.makeText(context, "ACTION_UP", Toast.LENGTH_SHORT).show();
					//Log.i("EVENT", "ACTION_UP event active without other event!");
					if (pmap.inMove) {
						pmap.inMove = false;
						//Log.i("UPDATEMAP", "reset loaded from mc");
						//pmap.getTileResolver().resetLoaded(); // this will secure every reload 12 tiles
						pmap.moveCoordinates(
								getRealPoint(event.getX(), scalePoint.x),
								getRealPoint(event.getY(), scalePoint.y));
						pmap.quickHack();
						//pmap.loadFromCache();
						//Log.i("UPDATEMAP", "updatescreen now");
						updateScreen();
						//pmap.reloadTiles();
					}
				}
				
				
				
				break;
			}
			
			return true;
			
		}
		
		public int getRealPoint(float eventPointX, int scalePointX) {
			float realPointX = (eventPointX-scalePointX)/BigPlanet.mapMagnification + scalePointX;
			return (int) realPointX;
		}
		
		public void onDown2(MotionEvent event, float x1, float y1, float x2, float y2) {
			mTouchDiagonalSize = Math.hypot((double)(x1 - x2), (double)(y1 - y2));
			mDown1X = x1;
			mDown1Y = y1;
			mDown2X = x2;
			mDown2Y = y2;
			//Log.i("EVENT", "onDown2 event active!");
			pmap.inMove = true;
		}
		
		public void onUp2(MotionEvent event) {
			up2_event_active = true;
			pmap.inMove = false;
			//Log.i("EVENT", "onUp2 is active");
			//Log.i("ZOOMS DATA", "Deftile: x is "+pmap.defTile.x+", y is "+pmap.defTile.y+" z is "+pmap.defTile.z);s
			szEngine.reloadMap.execute(new Float(mTouchScale));
			mTouchScale = 1;
			//pmap.quickHack();
			//pmap.loadFromCache();
			//updateScreen();
			//pmap.reloadTiles();
		}
		
		public void onMove2(MotionEvent event, float x1, float y1, float x2, float y2) {
			//Log.i("ONMOVE2", "x1: "+x1+" y1: "+y1+" x2: "+x2+" y2: "+y2);
			//Log.i("ONMOVE2", "mDown1X: "+mDown1X+" mDown1Y: "+mDown1Y+" mDown2X: "+mDown2X+" mDown2Y: "+mDown2Y);
			if(Math.abs(x1-mDown1X)+Math.abs(y1-mDown1Y) > 5 || Math.abs(x2-mDown2X)+Math.abs(y2-mDown2Y) > 5){
				pmap.inMove = true;
				mDown1X = x1;
				mDown1Y = y1;
				mDown2X = x2;
				mDown2Y = y2;
			    final double DiagonalSize = Math.hypot((double)(x1 - x2), (double)(y1 - y2));
			    mTouchScale = (DiagonalSize / mTouchDiagonalSize);
			    //Toast.makeText(context, "scale : "+mTouchScale, Toast.LENGTH_SHORT).show();
			    //Log.i("PMAP", "onMove2 event active!");			    
			    szEngine.updateScreen.execute(new Float(mTouchScale));
			}
		}


	}

	public void setMapSource(int sourceId) {
		//Log.i("TILE", "setMapSource "+sourceId);
		getPhysicalMap().getTileResolver().setMapSource(sourceId);
		getPhysicalMap().reloadTiles();
		updateScreen();
		markerManager.updateAll(PhysicMap.getZoomLevel());
	}
	
	public void clearInfoWin() {
		if(BigPlanet.isOpenInfoWin == true || BigPlanet.isOpenStatsWin == true) {
			BigPlanet.isOpenInfoWin = false;
			BigPlanet.isOpenStatsWin = false;
			MapControl.this.removeView(findViewById(R.id.infowindow));
			MapControl.this.removeView(findViewById(R.id.infowindowphoto));
			MapControl.this.removeView(findViewById(R.id.infowindowreadonly));
		}
	}
	
	
	private class TudingGestureListener implements OnGestureListener, OnDoubleTapListener {

		@Override
		public boolean onDown(MotionEvent arg0) {
			return false;
		}

		@Override
		public boolean onFling(MotionEvent arg0, MotionEvent arg1, float arg2,
				float arg3) {
			return false;
		}

		@Override
		public void onLongPress(MotionEvent arg0) {
			BigPlanet.resetPressAgainFlag();
			clearInfoWin();
			if(pmap.scaleFactor == 1) { // zoom has stopped
				longpress_event_active = true;
				pmap.inMove = false;
				//Log.i("EVENT", "longpress event active !");
				//BigPlanet.disabledAutoFollow(MapControl.this.context);
				if (onMapLongClickListener != null) {
					Point newoffset = pmap.NewOffset(arg0.getX(), arg0.getY());
					onMapLongClickListener.onMapLongClick(newoffset.x, newoffset.y);
					//onMapLongClickListener.onMapLongClick(0,0);
				}
			}
		}

		@Override
		public boolean onScroll(MotionEvent arg0, MotionEvent arg1, float arg2,
				float arg3) {
			//Toast.makeText(context, "onScroll", Toast.LENGTH_SHORT).show(); 
			return false;
		}

		@Override
		public void onShowPress(MotionEvent arg0) {
			// TODO Auto-generated method stub
			
		}

		@Override
		public boolean onSingleTapUp(MotionEvent arg0) {
			// TODO Auto-generated method stub
			//Toast.makeText(context, "Signle Tap up", Toast.LENGTH_SHORT).show();
			return false;
		}

		@Override
		public boolean onDoubleTap(MotionEvent arg0) {
			//Toast.makeText(context, "Double Tap", Toast.LENGTH_SHORT).show();
			doubletap_event_active = true;
			pmap.inMove = false;
			// 
			MapControl.this.pmap.TuneOffset(arg0.getX(), arg0.getY());
			MapControl.this.smoothZoom(1);
			return true;
		}

		@Override
		public boolean onDoubleTapEvent(MotionEvent arg0) {
			// TODO Auto-generated method stub
			return false;
		}

		@Override
		public boolean onSingleTapConfirmed(MotionEvent arg0) {
			// TODO Auto-generated method stub
			//Toast.makeText(context, "onSingleTapConfirmed", Toast.LENGTH_SHORT).show();
			int min_distanceX = 25;
			int min_distanceY = 25;
			Marker tapped_marker = null;
			if(BigPlanet.isGPSTracking) {
				for(Marker marker: MarkerManager.markersG) {
					int tmp_disX = (int)Math.abs(arg0.getX()-marker.offset_from_topleft.x);
					int tmp_disY = (int)Math.abs(arg0.getY()-marker.offset_from_topleft.y);
					if(min_distanceX > tmp_disX && min_distanceY > tmp_disY) {
						min_distanceX = tmp_disX;
						min_distanceY = tmp_disY;
						tapped_marker = marker;
					}
				}
				//if(tapped_marker != null) {
				//	Toast.makeText(context, "onSingleTapConfirmed 1 and tapped_marker is "+min_distanceX+" "+min_distanceY, Toast.LENGTH_SHORT).show();
				//}
				
			} else {
				if(MarkerManager.markersDB.size() > 0 || MarkerManager.savedTrackG.size() > 0 || MarkerManager.markersRecord.size() > 0) {
					for(Marker marker: MarkerManager.markersDB) {
						int tmp_disX = (int)Math.abs(arg0.getX()-marker.offset_from_topleft.x);
						int tmp_disY = (int)Math.abs(arg0.getY()-marker.offset_from_topleft.y);
						if(min_distanceX > tmp_disX && min_distanceY > tmp_disY) {
							min_distanceX = tmp_disX;
							min_distanceY = tmp_disY;
							tapped_marker = marker;
						}
					}
					for(Marker marker: MarkerManager.savedTrackG) {
						int tmp_disX = (int)Math.abs(arg0.getX()-marker.offset_from_topleft.x);
						int tmp_disY = (int)Math.abs(arg0.getY()-marker.offset_from_topleft.y);
						if(min_distanceX > tmp_disX && min_distanceY > tmp_disY) {
							min_distanceX = tmp_disX;
							min_distanceY = tmp_disY;
							tapped_marker = marker;
						}
					} 
					
					//if(tapped_marker != null) {
					//	Toast.makeText(context, "onSingleTapConfirmed 2 and tapped_marker is "+min_distanceX+" "+min_distanceY, Toast.LENGTH_SHORT).show();
					//}
				} 
			}
			
			for(Marker marker: MarkerManager.markersRecord) {
				int tmp_disX = (int)Math.abs(arg0.getX()-marker.offset_from_topleft.x);
				int tmp_disY = (int)Math.abs(arg0.getY()-marker.offset_from_topleft.y);
				if(min_distanceX > tmp_disX && min_distanceY > tmp_disY) {
					min_distanceX = tmp_disX;
					min_distanceY = tmp_disY;
					tapped_marker = marker;
				}
			} 
			
			
			if(MarkerManager.markers.size() > 0 ) {
				//TODO: 
				for(Marker marker: MarkerManager.markers) {
					if(marker.getMarkerType() == 0 || marker.getMarkerType() == 1) { // self or bookmark
						int tmp_disX = (int)Math.abs(arg0.getX()-marker.offset_from_topleft.x);
						int tmp_disY = (int)Math.abs(arg0.getY()-marker.offset_from_topleft.y);
						if(min_distanceX >= tmp_disX && min_distanceY >= tmp_disY) {
							min_distanceX = tmp_disX;
							min_distanceY = tmp_disY;
							tapped_marker = marker;
						}
					}
				}
				
				//if(tapped_marker != null) {
				//	Toast.makeText(context, "tapped_marker is "+min_distanceX+" "+min_distanceY, Toast.LENGTH_SHORT).show();
				//}
			}
			BigPlanet.resetPressAgainFlag();
			clearInfoWin();
			
			if(tapped_marker != null) {
				Log.i("Recording", "marker type is "+tapped_marker.getMarkerType());
				if(tapped_marker.getMarkerType() == MarkerManager.ACTION_NOTES_MARKER || tapped_marker.getMarkerType() == MarkerManager.ACTION_PHOTO_MARKER) { // 
					ActionNotes acttion_notes = new ActionNotes(context);
					infowin = new InfoWindow(context, acttion_notes, tapped_marker.actionType, tapped_marker.actionID, pmap.getWidth(), MapControl.this);
	                BigPlanet.isOpenInfoWin = true;
				} else if(tapped_marker.getMarkerType() == MarkerManager.BOOKMARK_MARKER) {
					//Log.i("TAP", "BOOKMARK_MARKER"+tapped_marker.actionID);
					DAO bookmark = new DAO(context);
					infowin = new InfoWindow(context, bookmark, tapped_marker.actionID, MapControl.this);
					BigPlanet.isOpenInfoWin = true;
				} else if(tapped_marker.getMarkerType() == MarkerManager.MY_LOCATION_MARKER) {
					if(BigPlanet.isOpenStatsWin == false)
						infowin = new InfoWindow(context, MapControl.this);
					BigPlanet.isOpenInfoWin = true;
					BigPlanet.isOpenStatsWin = true;
				} else if(tapped_marker.getMarkerType() == MarkerManager.RECORDED_MARKER) {
					//Log.i("Recording", "taped");
					infowin = new InfoWindow(context, MapControl.this, 0);
					BigPlanet.isOpenInfoWin = true;
				}
			}
	
			return true;
		}

	}
	
}
